#include "BTreeNode.h"

using namespace std;

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::read(PageId pid, const PageFile& pf)
{
	char buffer[PageFile::PAGE_SIZE];
	memset(buffer, 0, sizeof(buffer));
	pf.read(pid, buffer);
	memcpy(this, buffer, sizeof(BTNode));
	return 0;
}

/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::write(PageId pid, PageFile& pf)
{
	char buffer[PageFile::PAGE_SIZE];
	memset(buffer, 0, sizeof(buffer));
	memcpy(buffer, this, sizeof(BTNode));
	pf.write(pid, buffer);
	return 0;
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTNode::getKeyCount()
{
	return size;
}

int BTNode::getFatherPid()
{
	return fatherpid;
}

/*
 * Insert a (key, rid) pair to the node.
 * @param key[IN] the key to insert
 * @param rid[IN] the RecordId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTNode::insert(int key, const RecordId& rid)
{
	int index = 0;
	while (index < size && key > keyid[index])
		index++;
	if (size == 0)
	{
		size = 1;
		keyid[0] = key;
		recordid[0] = rid;
	}
	else
	{
		if (index != size)
		{
			for (int i=size; i>index; i--)
			{
				keyid[i] = keyid[i-1];
				recordid[i] = recordid[i-1];
			}
		}
		keyid[index] = key;
		recordid[index] = rid;
		size++;
	}
	return 0;
}

RC BTNode::insert(int key, const PageId& pid)
{
	int index = 0;
	while (index < size && key > keyid[index])
		index++;
	if (size == 0)
	{
		size = 1;
		keyid[0] = key;
		recordid[0].pid = pid;
		recordid[0].sid = 0;
	}
	else
	{
		size++;
		if (index != size)
		{
			for (int i=size; i>index; i--)
			{
				keyid[i] = keyid[i-1];
				recordid[i] = recordid[i-1];
			}
		}
		keyid[index] = key;
		recordid[index+1].pid = pid;
		recordid[index+1].sid = 0;
	}
	return 0;
}

/*
 * Insert the (key, rid) pair to the node
 * and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @param siblingKey[OUT] the first key in the sibling node after split.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::insertAndSplit(//int key, const RecordId& rid,
                              BTNode& sibling, int& siblingKey)
{
	//middle point is at first sibling
	//insert(key, rid);

	sibling.size = size - maxt - 1;
	sibling.nextpid = nextpid;
	sibling.fatherpid = fatherpid;
	sibling.setLoad(isload);
	for (int i=0; i<sibling.size; i++)
	{
		sibling.keyid[i] = keyid[maxt + i + 1];
		sibling.recordid[i] = recordid[maxt + i + 1];
	}

	if (leaf)
	{
		sibling.leaf = true;
		siblingKey = sibling.keyid[0];
		for (int i=maxt + 1; i<size; i++)
		{
			keyid[i] = 0;
			recordid[i].pid = 0;
			recordid[i].sid = 0;
		}
		size = maxt + 1;
	}
	else
	{
		sibling.leaf = false;
		sibling.recordid[sibling.size] = recordid[size];
		siblingKey = keyid[maxt];
		keyid[maxt] = 0;
		for (int i=maxt + 1; i<=size; i++)
		{
			keyid[i] = 0;
			recordid[i].pid = 0;
			recordid[i].sid = 0;
		}
		size = maxt;
	}
	return 0;
}

/*
 * Find the entry whose key value is larger than or equal to searchKey
 * and output the eid (entry number) whose key value >= searchKey.
 * Remeber that all keys inside a B+tree node should be kept sorted.
 * @param searchKey[IN] the key to search for
 * @param eid[OUT] the entry number that contains a key larger than or equalty to searchKey
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::locate(int searchKey, int& eid)
{
	for (int i=0; i<size; i++)
		if (keyid[i] >= searchKey)
		{
			eid = i;
			return 0;
		}
	eid = -1;
	return 0;
}

/*
 * Read the (key, rid) pair from the eid entry.
 * @param eid[IN] the entry number to read the (key, rid) pair from
 * @param key[OUT] the key from the entry
 * @param rid[OUT] the RecordId from the entry
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::readEntry(int eid, int& key, RecordId& rid)
{
	if (eid < 0 || eid >= size)
		return -1;
	else
	{
		key = keyid[eid];
		rid = recordid[eid];
	}
	return 0;
}

/*
 * Return the pid of the next slibling node.
 * @return the PageId of the next sibling node 
 */
PageId BTNode::getNextNodePtr()
{
	return nextpid;
}

/*
 * Set the pid of the next slibling node.
 * @param pid[IN] the PageId of the next sibling node 
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::setNextNodePtr(PageId pid)
{
	nextpid = pid;
	return 0;
}

RC BTNode::setFatherNodePtr(PageId pid)
{
	fatherpid = pid;
	return 0;
}

/*
 * Given the searchKey, find the child-node pointer to follow and
 * output it in pid.
 * @param searchKey[IN] the searchKey that is being looked up.
 * @param pid[OUT] the pointer to the child node to follow.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::locateChildPtr(int searchKey, PageId& pid)
{
	for (int i=0; i<size; i++)
		if (keyid[i] > searchKey)
		{
			pid = recordid[i].pid;
			return 0;
		}
	pid = recordid[size].pid;
	return 0;
}

/*
 * Initialize the root node with (pid1, key, pid2).
 * @param pid1[IN] the first PageId to insert
 * @param key[IN] the key that should be inserted between the two PageIds
 * @param pid2[IN] the PageId to insert behind the key
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::initializeRoot(PageId pid1, int key, PageId pid2)
{
	memset(keyid, 0, sizeof(keyid));
	memset(recordid, 0, sizeof(recordid));
	keyid[0] = key;
	recordid[0].pid = pid1;
	recordid[1].pid = pid2;
	leaf = false;
	size = 1;
	nextpid = fatherpid = -1;
	return 0;
}

RC BTNode::initializeRoot(PageId pid, int key, RecordId rid)
{
	memset(keyid, 0, sizeof(keyid));
	memset(recordid, 0, sizeof(recordid));
	keyid[0] = key;
	recordid[0] = rid;
	leaf = true;
	isload = true;
	size = 1;
	nextpid = fatherpid = -1;
	return 0;
}

bool BTNode::isLeaf()
{
	return leaf;
}

void BTNode::copy(BTNode s)
{
	size = s.getKeyCount();
	nextpid = s.getNextNodePtr();
	fatherpid = s.getFatherPid();
	leaf = s.isLeaf();
	isload = s.hasLoad();
	for (int i=0; i<size; i++) keyid[i] = s.keyid[i];
	for (int i=0; i<=size; i++) recordid[i] = s.recordid[i];
}

void BTNode::clear()
{
	size = 0;
	nextpid = -1;
	fatherpid = -1;
	leaf = false;
	memset(keyid, 0, sizeof(keyid));
	memset(recordid, 0, sizeof(recordid));
}

bool BTNode::hasLoad()
{
	return isload;
}

void BTNode::setLoad(bool v)
{
	isload = v;
}
